import std.stdio;
import std.file;
import std.algorithm;
import std.conv;
import std.range;
import std.string;

import core.bitop;
import std.algorithm, std.conv, std.exception,  std.functional,
    std.random, std.traits, std.typecons, std.typetuple;

dstring testStr = "
А психологически едва ли не самое важное, что пяроисходит с ребенком от трех до семи лет - это самосознание себя как части определенного социума. В это время ребенок окончательно убеждается, что он, как говорится, не пуп земли, и мир вовсе не вращается вокруг него.
";

void main(){
	string srcTextUtf8 = readText("/opt/neznaika").tolower();
	dstring srcText = to!dstring(srcTextUtf8);
	
	
	LangStats lstat = LangStats(srcText);
	lstat.writePostProbab();
	
	//write2Lifts(srcText);
	
	writeln("finished");
}

void appendMap(ref int[dstring] to, const int[dstring] from){
	foreach(k,v; from){
		to[k]=v;
	}
}

struct PostP{
	dstring str;
	double p;
}

struct StrLift{
	dstring str;
	double lift;
}


struct LangStats{
	int maxlen=149;
	
	dstring srcText;
	dchar[] allChars;
	int[dstring] all;
	byte[] indexesToScan;

	double[dstring] postProbMap;
	PostP[] postProbs;


	this(dstring srcText){
		this.srcText = srcText;
		bool[dchar] dchars;
		foreach( c; srcText ){
			dchars[c]=true;
		}
		allChars = dchars.keys;
		sort(allChars);
		
		foreach(c; allChars){
			write(c);
		}
		writeln();
		
		indexesToScan = new byte[srcText.length];

		foreach(i; 1..maxlen+1){
	   	  int[dstring] add = nSubstringsOnIndexes(srcText,i);
	   	  if( add.length==0 ){
	   	  	break;
	   	  }		
		  appendMap(all,add);
		}
		
		writeln(lift("незнайка"));
		writeln(lift("незнайку"));
		writeln(lift("незнайк"));
		writeln(lift("стол"));
		
		writeln(all[` течением времени `]);
		
		dstring phrase0  = tolower(`Городские жители сообразили, что с течением времени цена на акции может повыситься.`d);
		dstring phrase = phrase0;
		foreach(i; 0..10){
		  dstring core = biggestLift(phrase);
		  phrase = replace(phrase,core,"|"~toupper(core)~"|");
		  writeln('*',core,"*  ",lift(core));
		  writeln('*',phrase,'*');
	    }
		
		splitByMaxLeft(phrase0);


	    writeln();
	    writeln(testStr);
		splitByMaxLeft(tolower(testStr));

	    writeln(); 
		splitByMaxLeft(tolower(`я хочу идти домой`d));

		//dstring leftMax = biggestLiftLeft(phrase0);
		//writeln("Max left: ", leftMax, ` lift=`, lift(leftMax));
/*
		foreach(k,v; all){
			if( k.length>1 && k.length<maxlen ){
				double p = postProbab(k);
			  postProbs ~= PostP(k,  p);
			  postProbMap[k]=p;
		    }
		}
*/
	}
	
	void splitByMaxLeft(dstring str){
		dstring leftMax = biggestLiftLeft(str);
		writeln("Max left: [", leftMax, `] lift=`, lift(leftMax));
		if( leftMax=="" ){
			 return;
		}
		dstring tail = str[leftMax.length..$];
		splitByMaxLeft(tail);
	}
	
	dstring biggestLiftLeft(const dstring str){
		double maxlift=0;
		dstring subMax = "";
		foreach(i; 1..str.length+1){
			dstring sub = str[0..i];
			double l = lift(sub);
			//writeln(sub,`  `,l, ' ',str, ' ',i,' ',str.length);
			if( l >= maxlift ){
				maxlift=l;
				subMax = sub;
			}else{
				//sub = str[0..i-1];
				//break;
			}
		}
		return subMax;
	}
	
	dstring biggestLift(dstring str){
		dstring ret="";
		double maxlift=0;
		foreach(i; 0..str.length-2){
			foreach(j; i+2..str.length){
				dstring sub = str[i..j];
				double l = lift(sub);
				if( l>maxlift ){
					ret=sub;
					maxlift=l;
				}
			}
		}
		return ret;
	}
	
	double lift(dstring str){
		if( str !in all ){
			return 0;
		}
		double count = all[str];
		if( count<2 ){
			return 0;
		}
		double probS = 1.0 * count / srcText.length;
		foreach( size_t i; 0..str.length ){
			probS = probS / all[str[i..i+1]] * srcText.length;
		}
		return probS; 
	}
	
	
	int[dstring] nSubstringsOnIndexes(dstring s, int n){
		int[dstring] ret;
		int done=0;
		foreach(i; 0..s.length-n){
			if( indexesToScan[i]!=1 ){
			  dstring sub = s[i..i+n];	
			  ret[ sub ] ++;
			  done++;
			  //if( n==49 ) writeln(sub);
		    }
		}
		writeln("for ",n,` sunstrings done `, done); 
		foreach(i; 0..s.length-n){
			if( indexesToScan[i]!=1 ){
			  if( ret[ s[i..i+n] ]==1 ){
			  	indexesToScan[i]=1; // unique occurence, block further substrings at this point
			  }
		    }
		}
		return ret;
    }
	
	double postProbab(dstring full){
		double p = 1.0 * all[full] / all[full[0..$-1]];
		return p;
	}
	
	double maxPostProbab(dstring start){
		double maxv=0;
		foreach(c; allChars){
			dstring next = start ~ c;
			if( next in all ){
			  maxv = max(maxv, postProbab(next));
  		    }
		}
		return maxv;		
	}
	
	void writePostProbab(){
		

		sort!" a.p>b.p "(postProbs);
		foreach(sl;postProbs){
			if( sl.p>0.6 && all[sl.str]>1 ){
				double maxNext = maxPostProbab(sl.str);
				if( maxNext<0.2 ){
			       writeln('|',sl.str, "| ",sl.p, ' ',all[sl.str], `  maxNext=`,maxNext);
			  }
		    }
		}	

	}
}

void writeParts(dstring srcText){
	int[dstring] subs1 = nSubstrings(srcText,1);
	int[dstring] subs2 = nSubstrings(srcText,2);


	writeln(subs1);
	const(dchar)[][] subsList = subs2.keys;
	sort(subsList);
	foreach(c; subsList){
		writeln("|",c,"|");
	}
	
	testStr = testStr.tolower();
	writeln(testStr.length);
	foreach(i;0..testStr.length){
		if( i<testStr.length-1 ){
			auto sub = testStr[i..i+2];
			if( sub !in subs2 ){
				writeln("<<--ERR");
			} 
		}
		write(testStr[i]);
	}
	
	//wstring substr1 = srcText.find("За что же его отправили на Д");
	//writeln(1, substr1);
	
	// За что же его отправили на Дур��цкий остро
	
}


void write2Lifts(dstring srcText){
	int[dstring] subs1 = nSubstrings(srcText,1);
	int[dstring] subs2 = nSubstrings(srcText,2);

	StrLift[] lifts;
	foreach(k,v; subs2){
		lifts ~= StrLift(k,  1.0*srcText.length * v / subs1[k[0..1]] / subs1[k[1..2]] );
	}
	
	sort!" a.lift>b.lift "(lifts);
	foreach(sl;lifts){
		if( subs2[sl.str]>=3 ){
		  writeln(sl.str, ' ',sl.lift, ' ',subs2[sl.str]);
	    }
	}	
}

int[dstring] nSubstrings(dstring s, int n){
	int[dstring] ret;
	foreach(i; 0..s.length-n){
		ret[ s[i..i+n] ] ++;
	}
	return ret;
}